package cn.sc.summer.construction.route;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.StrUtil;
import cn.sc.summer.constant.enums.StatusEnum;
import cn.sc.summer.constant.model.ListChange;
import cn.sc.summer.constant.util.AssertUtil;
import cn.sc.summer.constant.util.ListUtilX;
import cn.sc.summer.core.user.SpringSecurityUserUtil;
import cn.sc.summer.constant.util.TreeUtilX;
import cn.sc.summer.daoapi.dao.SysApiRouteMapper;
import cn.sc.summer.daoapi.dao.SysRouteMapper;
import cn.sc.summer.daoapi.po.SysApiRoute;
import cn.sc.summer.daoapi.po.SysRoute;
import cn.sc.summer.domain.route.SysApiService;
import cn.sc.summer.domain.route.SysRouteService;
import cn.sc.summer.exception.enums.CommonExceptionEnum;
import cn.sc.summer.exception.exceptions.CommonException;
import cn.sc.summer.mybatis.annotation.TenantIgnore;
import cn.sc.summer.mybatis.annotation.dynamicdatasource.DataSource;
import cn.sc.summer.mybatis.enums.DataSourceEnum;
import cn.sc.summer.protocol.form.route.RouteBindForm;
import cn.sc.summer.protocol.form.route.RouteForm;
import cn.sc.summer.protocol.params.SysApiInfo;
import cn.sc.summer.protocol.vo.route.RouteVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 类名：路由管理实现类
 *
 * @author a-xin
 * @date 2024/1/4 10:43
 */
@Service
public class SysRouteServiceImpl implements SysRouteService {

    @Resource
    private SysRouteMapper sysRouteMapper;

    @Resource
    private SysApiRouteMapper sysApiRouteMapper;

    @Resource
    private SysApiService sysApiService;

    /**
     * 添加路由信息
     *
     * @param form 路由信息表单
     * @return 路由信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RouteVO addRoute(RouteForm form) {

        AssertUtil.functionIf(!form.getPId().equals("-1"), () -> Optional.ofNullable(sysRouteMapper.selectById(form.getPId()))
                .orElseThrow(() -> new CommonException("不存在对应的父级路由数据信息！")));

        CommonExceptionEnum.NORMAL_EXCEPTION.exceptionIf(sysRouteMapper.selectCount(new LambdaQueryWrapper<SysRoute>()
                .eq(SysRoute::getRoute, form.getRoute())
                .or()
                .eq(SysRoute::getCode, form.getCode())) > 0, "已存在相同的路由信息数据！");

        String dataTenantId = SpringSecurityUserUtil.getLoginUser().getTenantId();

        SysRoute sysRoute = new SysRoute();
        BeanUtils.copyProperties(form, sysRoute);
        //如果是第一层，直接新增，不用考虑左右值
        AssertUtil.functionIf(form.getPId().equals("-1"), () -> {
            sysRoute.setId(IdWorker.getIdStr());
            sysRoute.setLft(1)
                    .setRgt(2);
            sysRouteMapper.insert(sysRoute);
        }, () -> {//不是顶层，则考虑左右值变换

            //查询父级
            SysRoute pSysRoute = Optional.ofNullable(sysRouteMapper.selectOne(new LambdaQueryWrapper<SysRoute>()
                            .eq(SysRoute::getId, form.getPId())
                            .eq(SysRoute::getDataTenantId, dataTenantId)))
                    .orElseThrow(() -> new CommonException("不存在对应的权限组信息！"));

            //获取父级右值
            Integer pRgt = pSysRoute.getRgt();

            //如果父级的右值比左值大1，则说明该父级没有子级，直接左值+1，右值+2
            AssertUtil.functionIf((pSysRoute.getRgt() - pSysRoute.getLft()) == 1, () -> {
                sysRoute.setLft(pSysRoute.getLft() + 1);
                sysRoute.setRgt(pSysRoute.getLft() + 2);
            }, () -> {//走这里则说明该父级存在子级，获取父级右值-1的子级，利用子级的右进行+1，+2
                SysRoute sysRoute1 = sysRouteMapper.selectOne(new LambdaQueryWrapper<SysRoute>()
                        .eq(SysRoute::getRgt, pRgt - 1)
                        .eq(SysRoute::getDataTenantId, dataTenantId));
                //设置新权限组的  左右值
                sysRoute.setLft(sysRoute1.getRgt() + 1);
                sysRoute.setRgt(sysRoute1.getRgt() + 2);
            });

            sysRoute.setPId(pSysRoute.getPId());
            sysRoute.setDel(StatusEnum.ENABLE_OR_NOT_DELETED.getType());
            sysRoute.setDataTenantId(pSysRoute.getDataTenantId());
            sysRouteMapper.insert(sysRoute);

            //更新父级权限组的  右值
            pSysRoute.setRgt(pRgt + 2);
            sysRouteMapper.updateById(pSysRoute);

            //更新比父级右值大的所有权限组的左右值
            List<SysRoute> selectList = sysRouteMapper.selectList(new LambdaQueryWrapper<SysRoute>()
                    .eq(SysRoute::getDataTenantId, dataTenantId)
                    .and(qw -> qw.gt(SysRoute::getLft, pRgt)
                            .or()
                            .gt(SysRoute::getRgt, pRgt))
            );

            selectList.stream().forEach(sysRoute1 -> {
                if (sysRoute1.getId().equals(pSysRoute.getId())) {
                    return;
                }
                if (sysRoute1.getRgt() > pRgt && sysRoute1.getLft() > pRgt) {
                    sysRoute1.setLft(sysRoute1.getLft() + 2);
                    sysRoute1.setRgt(sysRoute1.getRgt() + 2);
                    sysRouteMapper.updateById(sysRoute1);
                    return;
                }
                if (sysRoute1.getLft() < pRgt && sysRoute1.getRgt() > pRgt) {
                    sysRoute1.setRgt(sysRoute1.getRgt() + 2);
                    sysRouteMapper.updateById(sysRoute1);
                }
            });

        });

        return BeanUtil.toBean(sysRoute, RouteVO.class);
    }

    /**
     * 更新路由信息
     *
     * @param form 路由信息表单
     * @return 路由信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RouteVO updateRoute(RouteForm form) {

        Optional.ofNullable(sysRouteMapper.selectById(form.getId()))
                .orElseThrow(() -> new CommonException("不存在对应的路由数据信息！"));

        CommonExceptionEnum.NORMAL_EXCEPTION.exceptionIf(sysRouteMapper.selectCount(new LambdaQueryWrapper<SysRoute>()
                .eq(SysRoute::getRoute, form.getRoute())
                .or()
                .eq(SysRoute::getCode, form.getCode())) > 0, "已存在相同的路由信息数据！");

        SysRoute sysRoute = new SysRoute();
        BeanUtils.copyProperties(form, sysRoute);
        sysRouteMapper.updateById(sysRoute);

        return BeanUtil.toBean(sysRoute, RouteVO.class);
    }

    /**
     * 删除路由信息
     *
     * @param id 路由ID
     */
    @Override
    public void deleteRoute(String id) {
        sysApiRouteMapper.exceptionIfExit(new LambdaQueryWrapper<SysApiRoute>()
                .eq(SysApiRoute::getRouteId, id), "该路由存在绑定关系，不能删除！");
        Optional.ofNullable(sysRouteMapper.selectById(id))
                .ifPresent(sysRoute -> {
                    sysRouteMapper.deleteById(sysRoute.getId());

                    String dataTenantId = SpringSecurityUserUtil.getLoginUser().getTenantId();

                    //获取父级权限组数据
                    SysRoute parentPermission = sysRouteMapper.selectOne(new LambdaQueryWrapper<SysRoute>()
                            .eq(SysRoute::getId, sysRoute.getPId()));
                    Integer pRgt = sysRoute.getRgt();

                    List<SysRoute> sysPermissionsList = sysRouteMapper.selectList(new LambdaQueryWrapper<SysRoute>()
                            .eq(SysRoute::getDataTenantId, dataTenantId)
                            .and(qw -> qw.gt(SysRoute::getLft, parentPermission.getLft())
                                    .or()
                                    .gt(SysRoute::getRgt, parentPermission.getLft()))
                    );

                    parentPermission.setRgt(parentPermission.getRgt() - 2);
                    sysRouteMapper.updateById(parentPermission);

                    AssertUtil.functionIfPre(sysPermissionsList, CollUtil::isNotEmpty, () -> {
                        sysPermissionsList.stream().forEach(permissions -> {
                            if (parentPermission.getId().equals(permissions.getId())) {
                                return;
                            }
                            if (permissions.getLft() > pRgt && permissions.getRgt() > pRgt) {
                                permissions.setLft(permissions.getLft() - 2);
                                permissions.setRgt(permissions.getRgt() - 2);
                                sysRouteMapper.updateById(permissions);
                                return;
                            }
                            if (permissions.getLft() < pRgt && permissions.getRgt() > pRgt) {
                                permissions.setRgt(permissions.getRgt() - 2);
                                sysRouteMapper.updateById(permissions);
                            }
                        });
                    });
                });
    }

    /**
     * 获取路由信息
     *
     * @param id 路由ID
     * @return 路由树结构
     */
    @Override
    @DataSource(DataSourceEnum.SLAVE)
    public List<Tree<String>> getRoute(String id) {

        AssertUtil.functionIfPre(id, StrUtil::isNotBlank, () -> Optional.ofNullable(sysRouteMapper.selectById(id))
                .orElseThrow(() -> new CommonException("不存在对应的路由信息！")));

        List<SysRoute> sysRoutes = sysRouteMapper.selectList(new LambdaQueryWrapper<SysRoute>()
                .eq(SysRoute::getDataTenantId, SpringSecurityUserUtil.getLoginUser().getTenantId()));

        return AssertUtil.functionIfPre(sysRoutes, CollUtil::isNotEmpty, () -> {
            TreeNodeConfig strConfig = TreeUtilX.getStrConfig(5);
            return TreeUtil.build(sysRoutes, StrUtil.isNotBlank(id) ? id : "-1", strConfig, (sysRoute, tree) -> {
                tree.setId(sysRoute.getId());
                tree.setParentId(sysRoute.getPId());
                tree.setName(sysRoute.getName());
                tree.putExtra("route", sysRoute.getRoute());
                tree.putExtra("open", sysRoute.getOpen());
                tree.putExtra("priority", sysRoute.getPriority());
            });
        }, new ArrayList<>());

    }

    /**
     * 路由绑定api信息
     *
     * @param form 路由权限组信息
     */
    @Override
    @TenantIgnore
    @Transactional(rollbackFor = Exception.class)
    public void routeBindApi(RouteBindForm form) {
        String routeId = form.getRouteId();
        Optional.ofNullable(sysRouteMapper.selectById(routeId))
                .orElseThrow(() -> new CommonException("不存在对应的路由信息!"));
        List<String> apiIds = form.getApiIds();
        sysApiService.checkApi(apiIds);

        List<SysApiRoute> sysApiRoutes =
                sysApiRouteMapper.selectList(SysApiRoute::getRouteId, form.getRouteId());

        AssertUtil.functionIfPre(sysApiRoutes, CollUtil::isNotEmpty, () -> {

            List<String> bindApiIds = sysApiRoutes.stream().map(SysApiRoute::getApiId)
                    .collect(Collectors.toList());

            ListChange<String> change = ListUtilX.getChange(apiIds, bindApiIds);
            List<String> needAdd = change.getNeedAdd();
            AssertUtil.functionIfPre(needAdd, CollUtil::isNotEmpty, () -> {
                List<SysApiRoute> sysApiRouteList = needAdd.stream().map(apiId -> {
                    SysApiRoute sysApiRoute = new SysApiRoute();
                    sysApiRoute.setApiId(apiId);
                    sysApiRoute.setRouteId(routeId);
                    return sysApiRoute;
                }).collect(Collectors.toList());
                sysApiRouteMapper.insertBatchSomeColumn(sysApiRouteList);
            });

            List<String> needDel = change.getNeedDel();
            AssertUtil.functionIfPre(needDel, CollUtil::isNotEmpty, () -> {
                sysApiRouteMapper.delete(new LambdaQueryWrapper<SysApiRoute>()
                        .eq(SysApiRoute::getRouteId, routeId)
                        .in(SysApiRoute::getApiId, needDel));
            });

        }, () -> {
            List<SysApiRoute> sysApiRouteList = apiIds.stream().map(apiId -> {
                SysApiRoute sysApiRoute = new SysApiRoute();
                sysApiRoute.setApiId(apiId);
                sysApiRoute.setRouteId(routeId);
                return sysApiRoute;
            }).collect(Collectors.toList());
            sysApiRouteMapper.insertBatchSomeColumn(sysApiRouteList);
        });
    }

    /**
     * 校验路由是否存在
     *
     * @param routeIds 路由ID
     */
    @Override
    @TenantIgnore
    public void checkRouteById(List<String> routeIds) {
        AssertUtil.functionIfPre(routeIds, CollUtil::isNotEmpty, () -> {
            int size = sysRouteMapper.selectList(new LambdaQueryWrapper<SysRoute>()
                    .in(SysRoute::getId, routeIds)).size();
            CommonExceptionEnum.NORMAL_EXCEPTION.exceptionIf(size != routeIds.size(), "不存在对应的路由信息!");
        });
    }

    /**
     * 获取所有路由对应绑定的api信息
     *
     * @param routeIds 路由信息
     * @return api信息
     */
    @Override
    @TenantIgnore
    public List<SysApiInfo> getRouteApiInfo(List<String> routeIds) {
        return AssertUtil.functionIfPre(routeIds, CollUtil::isNotEmpty, () -> {
            List<String> apiIds = sysApiRouteMapper.selectList(new LambdaQueryWrapper<SysApiRoute>()
                            .in(SysApiRoute::getRouteId, routeIds))
                    .stream().map(SysApiRoute::getApiId).collect(Collectors.toList());
            return sysApiService.getApiInfo(apiIds);
        }, new ArrayList<>());
    }

}
